import java.awt.Color;
import java.awt.Graphics2D;
import java.util.*;

public class Map {
	
	public Map (int height, int width) {
		
		if (height > 0 && width > 0) {
			this.height = height;
			this.width = width;
		}
		else {
			this.height = this.width = 5;
		}
		
		horOffset = (int)(sqrtThree*Tile.side/2.0);
		
		rows = new ArrayList<ArrayList<Tile>>( this.height );
		
		for (int row=0; row < this.height; row++) {
			rows.add(row, new ArrayList<Tile>(this.width));
			
			for (int column=0; column < this.width; column++) {
				double x, y;
				x = (column - ((row%2) - 1)/2.0)*horOffset*2;//sqrtThree*Tile.side;
				y = (3*row + 1)*Tile.side/2.0;
				rows.get(row).add(column, new Tile((int)x, (int)y));
			}
		}		
		
		for (int row=0; row < this.height; row++) {
			for (int column=0; column < this.width; column++) {
				Tile[] neighbours = new Tile[6];
				if (row%2 == 1) {
					neighbours[0] = getTile(row-1, column);
					neighbours[1] = getTile(row, column+1);
					neighbours[2] = getTile(row+1, column);
					neighbours[3] = getTile(row+1, column-1);
					neighbours[4] = getTile(row, column-1);
					neighbours[5] = getTile(row-1, column-1);
				}
				else {
					neighbours[0] = getTile(row-1, column+1);
					neighbours[1] = getTile(row, column+1);
					neighbours[2] = getTile(row+1, column+1);
					neighbours[3] = getTile(row+1, column);
					neighbours[4] = getTile(row, column-1);
					neighbours[5] = getTile(row-1, column);
				}
				getTile(row, column).setNeighbours(neighbours);
			}
		}
		
		spawn = getTile(0, 0);
		spawn.isSpawn(true);
		destination = getTile(height-1, width-1);
		destination.isDestination(true);
		
		for (int row=0; row < this.height; row++) {
			for (int column=0; column < this.width; column++) {
				Tile tile = getTile(row, column);
				tile.H = calculateHeuristic(tile);
			}
		}
	}
	
	
	private Tile getTile(int row, int column) {
		boolean isInRange = (row >= 0) && (row < this.height) && 
							(column >= 0) && (column < this.width);
		if (isInRange)
			return rows.get(row).get(column);
		else
			return null;
	}
	
	
	public void render(Graphics2D g2) {
		for (int row=0; row < this.height; row++) {			
			for (int column=0; column < this.height; column++) {
				getTile(row, column).render(g2);
			}
		}
		g2.drawLine(spawn.getX(), spawn.getY(), destination.getX(), destination.getY());
	}
	
	
	public void setSpawn(int row, int column) {
		spawn.isSpawn(false);
		spawn = getTile(row, column);
		spawn.isSpawn(true);
	}
	
	
	public void setDestination(int row, int column) {
		destination.isDestination(false);
		destination = getTile(row, column);
		destination.isDestination(true);
	}
	
	
	public void toggleObstacle(int row, int column) {
		Tile target = getTile(row, column);
		target.isWalkable( !target.isWalkable() );
	}
	
	
	public boolean calculatePath() {
		boolean foundPath = false;
		int row, column;
		//start algorithm
		row = column = 0;
		Tile current = destination;
		ArrayList<Tile> openlist = new ArrayList<Tile>(this.width);
		openlist.add(current);
		ArrayList<Tile> closedlist = new ArrayList<Tile>(this.width);
		
		while (!openlist.isEmpty()) {
			current = openlist.get(0);
			for (int i=1; i < openlist.size(); i++) {
				if (openlist.get(i).F <= current.F)
					current = openlist.get(i);
			}
			
			for (int i=0; i < 6; i++) {
				if (current.neighbours[i] != null && current.neighbours[i].isWalkable()) {
					if (current.neighbours[i].G >= (current.G + 10) || (current.neighbours[i].G == 0 && !current.neighbours[i].isDestination())) {
						current.neighbours[i].G = current.G + 10;
						current.neighbours[i].parent = current;
					}
					current.neighbours[i].F = current.neighbours[i].H + current.neighbours[i].G;
					
					if (!closedlist.contains(current.neighbours[i])) {
						openlist.add(current.neighbours[i]);
					}
				}
			}
			openlist.remove(current);
			closedlist.add(current);
		}
		current = spawn.parent;
		while (!current.isDestination()) {
			current.setFillColor(Color.blue);
			current = current.parent;
		}
		//end algorithm
		return foundPath;
	}
	
	
	private int calculateHeuristic(Tile start) {
		int heuristic = 0;
		int startX, startY, destX, destY;
		startX = start.getX();
		startY = start.getY();
		destX = spawn.getX();
		destY = spawn.getY();
		
		while (startX != destX || startY != destY) {
			heuristic += 10;
			if (heuristic>30000)
				break;
			if (startX > destX)
				startX -= horOffset;
			else if (startX < destX)
				startX += horOffset;
			if (startY > destY)
				startY -= (int)(3.0/2.0*Tile.side);
			else if (startY < destY)
				startY += (int)(3.0/2.0*Tile.side);
			else if (startX > destX)
				startX -= horOffset;
			else if (startX < destX)
				startX += horOffset;
		}
		return heuristic;
	}
	
	
	private int height, width;
	private double sqrtThree = 1.7320508075689;
	private int horOffset;
	private ArrayList<ArrayList<Tile>> rows;
	private Tile spawn, destination;
}
